use async_trait::async_trait;
use std::collections::HashMap;

use axum::{
    http::StatusCode,
    response::{IntoResponse, Response},
};
use serde::{Deserialize, Serialize};
use utoipa::ToSchema;

use crate::prelude::ApiResponse;

#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct ValidationErrorDetails {
    #[schema(example = "Validation failed")]
    pub message: String,

    #[schema(example = json!({"username": "用户名不能为空", "email": "邮箱格式不正确"}))]
    pub field_errors: HashMap<String, String>,
}

impl From<&validator::ValidationErrors> for ValidationErrorDetails {
    fn from(errors: &validator::ValidationErrors) -> Self {
        let mut field_errors = HashMap::<String, String>::new();

        for (field, errs) in errors.field_errors() {
            let field_messages: Vec<String> = errs
                .iter()
                .map(|error| {
                    if let Some(msg) = &error.message {
                        msg.to_string()
                    } else {
                        format!("字段 '{}' 验证失败", field)
                    }
                })
                .collect();
            field_errors.insert(field.to_string(), field_messages.join(", "));
        }

        ValidationErrorDetails {
            message: errors.to_string(),
            field_errors,
        }
    }
}

#[derive(thiserror::Error, Debug, Clone, Serialize, Deserialize, ToSchema)]
pub enum BizError {
    #[error("Internal server error")]
    InternalError,

    #[error("Not found: {0}")]
    NotFoundError(String),

    #[error("Bad request: {0}")]
    BadRequestError(String),

    #[error("Unauthorized")]
    UnauthorizedError,

    #[error("jwt：{0}")]
    JwtTokenError(&'static str),

    #[error("Forbidden")]
    ForbiddenError,

    #[error("Database error: {0}")]
    DatabaseError(String),

    #[error("Redis error: {0}")]
    RedisError(String),

    #[error("Queue error: {0}")]
    QueueError(String),

    #[error("Validation error")]
    ValidationError(ValidationErrorDetails),

    #[error("Business Error: {0}")]
    BusinessError(String),
}

impl BizError {
    pub fn http_code(&self) -> StatusCode {
        match &self {
            BizError::NotFoundError(_) => StatusCode::NOT_FOUND,
            BizError::BadRequestError(_) => StatusCode::BAD_REQUEST,
            BizError::UnauthorizedError => StatusCode::UNAUTHORIZED,
            BizError::ForbiddenError => StatusCode::FORBIDDEN,
            BizError::ValidationError(_) => StatusCode::BAD_REQUEST,
            _ => StatusCode::INTERNAL_SERVER_ERROR,
        }
    }

    pub fn http_code_u32(&self) -> u32 {
        self.http_code().as_u16() as u32
    }

    pub fn detail_errors(&self) -> Option<serde_json::Value> {
        match &self {
            BizError::ValidationError(details) => {
                Some(serde_json::to_value(&details.field_errors).unwrap_or_default())
            }
            _ => None,
        }
    }
}

#[async_trait]
impl IntoResponse for BizError {
    fn into_response(self) -> Response {
        ApiResponse::<()>::response_biz_error(self).into_response()
    }
}

impl From<validator::ValidationErrors> for BizError {
    fn from(err: validator::ValidationErrors) -> Self {
        BizError::ValidationError(ValidationErrorDetails::from(&err))
    }
}

macro_rules! impl_from_error {
    ($($ty:ty => $variant:path),+) => {
        $(impl From<$ty> for BizError {
            fn from(err: $ty) -> Self {
                $variant(err.to_string())
            }
        })+
    };
}
impl_from_error!(
    anyhow::Error => BizError::BadRequestError,
    sea_orm::DbErr => BizError::DatabaseError,
    serde_json::Error => BizError::BadRequestError
);
